#pragma once

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
#include<queue>

using namespace std;

namespace ns_blockqueue
{
    template<class T>
    class BlockQueue
    {
    public:
        BlockQueue(const int count = 5)
            :_cap(count)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
            pthread_cond_init(&_is_full, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_empty);
            pthread_cond_destroy(&_is_full);
        }
        //const& :输入
        //*:输出
        //&:输入输出
        void Push(const T& in)
        {
            LockQueue();
            //临界区
            while(IsFull())
            {
                //等待的，把线程挂起，我们当前是持有锁的！！！
                ProducterWait();
            }
            //向队列放数据，生产函数
            _bq.push(in);
            WakeUpConsumer();
            UnlockQueue();
        }
        void Pop(T* out)
        {
            LockQueue();
            while(IsEmpty())
            {
                //无法消费
                ConsumerWait();
            }
            //从队列里拿数据，消费函数
            *out = _bq.front();
            _bq.pop();
            WakeUpProduct();
            UnlockQueue();
        }
    private:
        bool IsFull()
        {
            return _bq.size() == _cap;
        }
        bool IsEmpty()
        {
            return _bq.empty();
        }
        void LockQueue()
        {
            pthread_mutex_lock(&_mtx);
        }
        void UnlockQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }
        void ProducterWait()
        {
            //1. 调用时，会首先自动释放锁，然后再挂起自己
            //2. 返回时，会首先自动竞争锁，获取到锁时，才能返回！
            pthread_cond_wait(&_is_empty, &_mtx);
        }
        void ConsumerWait()
        {
            pthread_cond_wait(&_is_full, &_mtx);
        }
        void WakeUpProduct()
        {
            pthread_cond_signal(&_is_empty);
        }
        void WakeUpConsumer()
        {
            pthread_cond_signal(&_is_full);
        }
    private:
        queue<T> _bq;//阻塞队列
        int _cap;//队列元素上限
        pthread_mutex_t _mtx;//保护临界资源的锁
        //1. 当生产满的时候，就不要再生产了（不要竞争锁了），而是应该让消费者来消费
        //2. 当消费光的时候，就不要再消费了（不要竞争锁了），而是应该让生产者来生产
        pthread_cond_t _is_empty;//_bq空了，生产者者要在该条件变量下等待是否为空
        pthread_cond_t _is_full;//_bq满了，消费者者要在该条件变量下等待是否为满
    };
}
